#include<stdio.h>
#include<stdlib.h>
#include<assert.h>
#include<unistd.h>
#include<string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <ctype.h>
#include <sys/stat.h>
#include <fcntl.h>


#define LINE_SIZE 1024
#define OPTION_SIZE 32
#define LEFT "["
#define RIGHT "]"
#define LABLE "# "
#define DELIM " \t"
#define OUT_RDIR 1 
#define IN_RDIR  2
#define APPEND_RDIR 3 

int rdir = 0;
int lastcode = 0;
char pwd[LINE_SIZE];
char command[LINE_SIZE];
char* argv[OPTION_SIZE];
char penv[OPTION_SIZE];
char* filename;

const char*getusr()
{
    return getenv("USER");
}
const char*gethost()
{
    return getenv("HOSTNAME");
}
void getpwd()
{
    getcwd(pwd,LINE_SIZE);
}
void check_command(char * pos)
{
    while(*pos)//遍历指令
    {
        if(*pos == '<')//输入重定向
        {
            rdir = IN_RDIR;
            *pos++ = '\0';
            while(isspace(*pos)) pos++;//跳过
            filename = pos;
            break;
        }
        else if((*pos)== '>')//输出重定向
        {
            *pos++ = '\0'; 
            if((*pos)== '>')//追加重定向
            {  
                rdir = APPEND_RDIR;
                *pos++ = '\0';
                while(isspace(*pos)) pos++;//跳过
                    filename = pos;
                break;
            }
            rdir = OUT_RDIR;
            while(isspace(*pos)) pos++;//跳过
            filename = pos;            
            break;
        }
        else 
        {
            ;
        }
        pos++;
    }

}
int interact()
{
    //打印bash命令行USR+主机名+路径
    getpwd();
    // printf(LEFT"%s@%s %s"RIGHT LABLE,getusr(),gethost(),pwd);

    printf(LEFT"%s@%s %s"RIGHT LABLE,getusr(),gethost(),getenv("PWD"));
    
    fgets(command,LINE_SIZE,stdin);
    //ls -a\n去除\n
    command[strlen(command) - 1] = '\0';
   // printf("%s",command);
   //检查重定向符号 
   check_command(command);
   return strlen(command)-1;
}
void splitstring()
{
    //"ls -a -l"
    int i = 0;
    argv[i++] = strtok(command,DELIM); 
    while(argv[i++] = strtok(NULL,DELIM));
    
}
void NormalExecute()
{
    //创建子进程
    int id = fork();
    if(id < 0)
    {
        perror("fork:");
    }
    else if(id == 0)//子进程
    {
        if(rdir == OUT_RDIR)
        {
            printf("%s\n",filename);
            int fd = open(filename,O_WRONLY|O_CREAT|O_TRUNC,0660);
            dup2(fd,1);
        }
        else if(rdir == IN_RDIR)
        {
            printf("%s\n",filename);
            int fd = open(filename,O_RDONLY,0660);
            dup2(fd,0);

        }
        else if(rdir == APPEND_RDIR)
        {
            printf("%s\n",filename);
            int fd = open(filename,O_WRONLY|O_CREAT|O_APPEND,0660);
            dup2(fd,1);
        }
        //程序替换
        execvp(argv[0],argv);
        exit(2);
    }
    else//父进程
    {
        int status = 0;
       // printf("我是父进程,开始等待\n");
        int ret = waitpid(id,&status,0);//阻塞等待
       // printf("退出码: %d,信号: %d\n",status >>8 &0xff,status & 0x7f);
        lastcode = WIFEXITED(status);
       // printf("退出码: %d,信号: %d\n",WIFEXITED(status),WEXITSTATUS(status));
    }
}
int BuildCommand(int argc)
{
    if(strcmp(argv[0],"ls")==0 )
    {
        argv[argc++] = "--color";
       // printf("%s\n",argv[argc-1]);
        argv[argc] = NULL;
    }
    if(strcmp(argv[0],"cd") == 0)
    {
       chdir(argv[1]);
       //改工作目录
       getpwd();
       return 1;
    }
    else if(strcmp(argv[0],"echo")== 0) 
    {
        if(strcmp(argv[1],"$?")==0)//输出退出码
        {
            printf("%d\n", lastcode);
            lastcode = 0;
            return 1;
        }
       else if(strcmp(argv[1]+1,"$")== 0)//输出环境变量
        {
            printf("%s",getenv(argv[1]+1));
            return 1;
        }
    }
    else if(strcmp(argv[0],"export")== 0)
    {
       memcpy(penv,argv[1],strlen(argv[1]));
      // printf("%s\n",penv);
       //putenv(argv[1]);不能直接put argv存的是command的,会失效
       putenv(penv);  
    }
    return 0;
}
int main()
{
    while(1)
    {
        //1 初始化
        rdir = 0;
        filename = NULL;
        //2: 人机互动,接收指令
        int argc = interact();
        if(!argc)//argc为零,无指令
        {
            continue;
        }
        //3: 分割字符串
        splitstring();
        //4 内建命令
       int ret = BuildCommand(argc); 
        //5 执行普通指令
       if(!ret)
         NormalExecute();
    }
    return 0;
}
